﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Timers;

namespace CSharpHelpers.Sockets
{
    public delegate void SocketClientMessageDelegate(SocketMessage message);

    /// <summary>
    /// Socket客户端类
    /// </summary>
    public class SocketClient : SocketBase
    {
        public Socket skClient;

        private SocketMessage message;
        private SocketClientMessageDelegate messageDelegate;

        private Thread reveiveThread;
        private static System.Timers.Timer timerReConnect;
        private static System.Timers.Timer timerHeartBeat;

        private object myObject = new object();
        private object myLock = new object();
        private object myLockObject = new object();
        private object lockOject = new object();

        public SocketMessageQueue<Byte[]> m_workQueue;

        /// <summary>
        /// 实例化Socket客户端
        /// </summary>
        /// <param name="msgDelegate">消息委托</param>
        /// <param name="serverIP">服务器IP</param>
        /// <param name="ServerPort">服务器端口</param>
        /// <param name="reconnectInterval"></param>
        /// <param name="reconnectTimes"></param>
        public SocketClient(SocketClientMessageDelegate msgDelegate, string serverIP, int ServerPort, int reconnectInterval, int reconnectTimes)
        {
            message = new SocketMessage();

            this.messageDelegate = msgDelegate;
            m_ServerIP = serverIP;
            m_ServerPort = ServerPort;
            m_ReconnectTimes = reconnectTimes;
            reveiveThread = new Thread(new ThreadStart(ReceiveData));
            reveiveThread.IsBackground = true;
            reveiveThread.Name = "SocketReceiveData";

            timerReConnect = new System.Timers.Timer(reconnectInterval);
            timerReConnect.Elapsed += new ElapsedEventHandler(DoReconnect);

            timerHeartBeat = new System.Timers.Timer();
            timerHeartBeat.Interval = 5000;
            timerHeartBeat.Elapsed += new ElapsedEventHandler(OnHeartBeat);

            m_workQueue = new SocketMessageQueue<Byte[]>();
            m_workQueue.UserWork += new UserWorkEventHandler<Byte[]>(HandleMessageQueue);
            m_workQueue.StartThread();
        }


        #region 连接
        /// <summary>
        /// 创建Socket连接
        /// </summary>
        /// <returns></returns>
        public bool Connect()
        {
            try
            {
                skClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                skClient.Connect(m_ServerIP, m_ServerPort);
                m_IsConnected = true;

                //启动线程，开始监听
                StartThread();

                //连接成功开始心跳
                StartHeartBeat();

                //启动重连机制
                StartReConnect();


                //连接服务器成功，执行回调事件   
                message.Type = SocketMessageType.Connected;
                message.Message = "";

                if (messageDelegate != null)
                {
                    messageDelegate(message);
                }

                return true;
            }
            catch
            {
                m_IsConnected = false;
                StopHeartBeat();

                StartReConnect();

                return false;
            }
        }

        /// <summary>
        /// 重连
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void DoReconnect(object sender, ElapsedEventArgs e)
        {
            lock (myLock)
            {
                ReConnectCounter++;

                if (ReConnectCounter > this.m_ReconnectTimes)
                {
                    //超过15s，没有收到服务器发过来的任何信息，则断开连接
                    ReConnectCounter = 0;

                    message.Type = SocketMessageType.Disconnected;
                    message.Message = "";

                    if (messageDelegate != null)
                    {
                        messageDelegate(message);
                    }

                    m_IsConnected = false;
                    m_HaveShowConnecting = false;
                    StopHeartBeat();
                    Close();

                    //重连
                    Connect();
                }
                else
                {
                    if (m_IsConnected && !m_HaveShowConnecting)
                    {
                        m_HaveShowConnecting = true;
                    }
                }
            }
        }

        /// <summary>
        /// 断掉Sokect
        /// </summary>
        public void Close()
        {
            skClient.Close();
            skClient.Dispose();
        }


        /// <summary>
        /// 销毁Sokect
        /// </summary>
        public void Dispose()
        {
            StopHeartBeat();
            StopReConnect();
            skClient.Close();
            skClient.Dispose();
        }

        /// <summary>
        /// 启动重连机制
        /// </summary>
        public void StartReConnect()
        {
            if (timerReConnect.Enabled == false)
            {
                timerReConnect.Start();
            }
        }

        /// <summary>
        /// 停止重连
        /// </summary>
        public void StopReConnect()
        {
            if (timerReConnect.Enabled == true)
            {
                timerReConnect.Stop();
            }
        }

        public bool isConnect()
        {
            return m_IsConnected;
        }
        #endregion

        #region 心跳
        /// <summary>
        /// 开始心跳
        /// </summary>
        public void StartHeartBeat()
        {
            if (timerHeartBeat.Enabled == false)
            {
                timerHeartBeat.Start();
            }
        }

        /// <summary>
        /// 保持心跳
        /// </summary>
        public void OnHeartBeat(object sender, ElapsedEventArgs e)
        {
            //发送头部信息保持心跳
            SendMessage(m_header);
        }

        /// <summary>
        /// 停止心跳
        /// </summary>
        public void StopHeartBeat()
        {
            if (timerHeartBeat.Enabled == true)
            {
                timerHeartBeat.Stop();
            }
        }
        #endregion

        #region 接收
        /// <summary>
        /// 开始线程
        /// </summary>
        public void StartThread()
        {
            if (reveiveThread != null && reveiveThread.ThreadState == (ThreadState.Background | ThreadState.Unstarted))
            {
                reveiveThread.Start();
            }
        }

        /// <summary>
        /// 20ms接收一次Socket数据
        /// </summary>
        public void ReceiveData()
        {
            while (true)
            {
                try
                {
                    if (skClient.Available > 0) //有数据接收 begin
                    {
                        lock (myObject)
                        {
                            //重新计算时间，超过15s，没有收到服务器发过来的任何信息，则断开连接
                            ReConnectCounter = 0;

                            //Receive数据后，不管有没有另外出来，socket都会把数据丢掉，如果不Receive，则一直存在套接字中，Available会增加
                            byte[] bt = new byte[skClient.Available];
                            skClient.Receive(bt);

                            EnSocketMessageQueue(bt, bt.Length);
                        }

                    }//有数据接收 end
                }
                catch
                { }

                Thread.Sleep(20);
            }

        }
        #endregion

        #region 拆分及拼装消息
        /// <summary>
        /// 拆分及拼装消息
        /// 把消息压入Socket消息队列
        /// </summary>
        /// <param name="btBuf"></param>
        /// <param name="ReceivedBytesLen"></param>
        /// <param name="m_workQueue"></param>
        public void EnSocketMessageQueue(byte[] btBuf, int ReceivedBytesLen)
        {
            string msgVaule = string.Empty;

            lock (myLockObject)
            {
                //1.已经没有半截剩余信息
                if (m_ReceivedBufferOffset == 0)//Means no half message remained
                {
                    #region
                    if (ReceivedBytesLen >= MESSAGE_HEADER_SIZE)	//#1
                    {
                        int Bytes = ReceivedBytesLen;
                        int Offset = 0;

                        //至少执行一次
                        while (Bytes >= MESSAGE_HEADER_SIZE)		//#2
                        {
                            int MessageMark = SocketBufferHelper.GetMessageMark(btBuf, Offset);
                            int MessageSize = SocketBufferHelper.GetMessageSize(btBuf, Offset);

                            //If an invalid message header, ignore it
                            if (MessageMark != MESSAGE_HEADER_MARK || MessageSize < 0 || MessageSize > MAX_MESSAGE_SIZE || ReceivedBytesLen > btBuf.Length)
                            {
                                return;
                            }

                            //如果字节数 >= 信息长度+信息头长度的和
                            if (Bytes >= MessageSize + MESSAGE_HEADER_SIZE)
                            {
                                byte[] byCommand = HanldeCommand(btBuf, Offset, MessageSize + MESSAGE_HEADER_SIZE);
                                m_workQueue.Enqueue(byCommand);

                                //则字节数 -（信息长度+信息头长度的和）
                                Bytes -= MessageSize + MESSAGE_HEADER_SIZE;
                                Offset += MessageSize + MESSAGE_HEADER_SIZE;

                                //Lyle(20110927): Add heartbeat function
                                //m_HeartBeatTimeStamp = Active::TimeGetTime();
                                //m_HeartBeatTimeStampForSend = m_HeartBeatTimeStamp;
                            }
                            else
                            {
                                // Handle the case of "MESSAGE_HEADER_SIZE  > 0 && Byte < MESSAGE_HEADER_SIZE+MessageSize"
                                SocketBufferHelper.CopyToMsgBuffer(MESSAGE_BUFFER, btBuf, m_ReceivedBufferOffset, Offset, Bytes);
                                m_ReceivedBufferOffset += Bytes;
                                Bytes = 0;
                                Offset += Bytes;
                            }

                        } //end while #2

                        // Handle the case of "Bytes  > 0 && Byte < MESSAGE_HEADER_SIZE", store the spare buffer to the receiving buffer so that it can combine with next received message
                        if (Bytes > 0)
                        {
                            SocketBufferHelper.CopyToMsgBuffer(MESSAGE_BUFFER, btBuf, m_ReceivedBufferOffset, Offset, Bytes);
                            m_ReceivedBufferOffset += Bytes;
                        }

                    } //#1
                    else
                    {
                        SocketBufferHelper.CopyToMsgBuffer(MESSAGE_BUFFER, btBuf, m_ReceivedBufferOffset, 0, ReceivedBytesLen);
                        m_ReceivedBufferOffset += ReceivedBytesLen;
                    }
                    #endregion
                }
                else
                {
                    //2.还有半截剩余信息
                    if (m_ReceivedBufferOffset + ReceivedBytesLen > RECEIVED_BUFFER_LEN)
                    {
                        //LOG_TO("ActiveClient: DoReceive buffer size error.", GetTodayLogPath());
                        return;
                    }

                    //拼接
                    SocketBufferHelper.CopyToMsgBuffer(MESSAGE_BUFFER, btBuf, m_ReceivedBufferOffset, 0, ReceivedBytesLen);

                    m_ReceivedBufferOffset += ReceivedBytesLen;

                    //char* Buffer = m_ReceivedBuffer;
                    int TotalBytes = m_ReceivedBufferOffset;
                    int Bytes = m_ReceivedBufferOffset;
                    int Offset = 0;

                    //IMPORTANT
                    if (TotalBytes >= MESSAGE_HEADER_SIZE)	//#1
                    {
                        //临时Buffer的起始位置
                        m_ReceivedBufferOffset = 0;

                        while (Bytes >= MESSAGE_HEADER_SIZE)		//#2
                        {
                            int MessageMark = SocketBufferHelper.GetMessageMark(MESSAGE_BUFFER, Offset);
                            int MessageSize = SocketBufferHelper.GetMessageSize(MESSAGE_BUFFER, Offset);

                            //If an invalid message header, ignore it and clear up the received buffer
                            if (MessageMark != MESSAGE_HEADER_MARK || MessageSize < 0)
                            {
                                return;
                            }

                            if (Bytes >= MessageSize + MESSAGE_HEADER_SIZE)
                            {
                                byte[] byCommand = HanldeCommand(MESSAGE_BUFFER, Offset, MessageSize + MESSAGE_HEADER_SIZE);
                                m_workQueue.Enqueue(byCommand);

                                Bytes -= MessageSize + MESSAGE_HEADER_SIZE;
                                Offset += MessageSize + MESSAGE_HEADER_SIZE;

                                if (Bytes == 0)
                                {
                                    m_ReceivedBufferOffset = 0;
                                }

                                //Lyle(20110927): Add heartbeat function
                                //m_HeartBeatTimeStamp = Active::TimeGetTime();
                                //m_HeartBeatTimeStampForSend = m_HeartBeatTimeStamp;
                            }
                            else
                            {
                                if ((m_ReceivedBufferOffset + Bytes) > RECEIVED_BUFFER_LEN)
                                {
                                    m_ReceivedBufferOffset = 0;
                                    return;
                                }
                                else
                                {
                                    //重新替换一遍拼接的内容
                                    SocketBufferHelper.CopyToMsgBuffer(MESSAGE_BUFFER, btBuf, m_ReceivedBufferOffset, Offset, ReceivedBytesLen);
                                    m_ReceivedBufferOffset += Bytes;
                                    Bytes = 0;
                                    Offset += Bytes;
                                }
                            }

                        } //end while #2

                        // Handle the case of "Bytes  > 0 && Byte < MESSAGE_HEADER_SIZE", store the spare buffer to the receiving buffer so that it can combine with next received message
                        if (Bytes > 0)
                        {
                            if ((m_ReceivedBufferOffset + Bytes) > RECEIVED_BUFFER_LEN)
                            {
                                m_ReceivedBufferOffset = 0;
                                return;
                            }

                            SocketBufferHelper.CopyToMsgBuffer(MESSAGE_BUFFER, btBuf, m_ReceivedBufferOffset, Offset, ReceivedBytesLen);
                            m_ReceivedBufferOffset += Bytes;
                        }
                    }
                }
            }
        }


        private byte[] HanldeCommand(byte[] sourceBuffer, int offset, int len)
        {
            if (len > MAX_MESSAGE_SIZE)
            {
                return null;
            }

            byte[] btCommad = new byte[len];

            for (int i = 0; i < len; i++)
            {
                btCommad[i] = sourceBuffer[offset + i];
            }

            return btCommad;
        }

        #endregion

        #region 处理消息
        /// <summary>
        /// 处理消息,回调命令事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void HandleMessageQueue(object sender, SocketMessageQueue<Byte[]>.EnqueueEventArgs e)
        {
            lock (lockOject)
            {
                if (e.Item.Length == 8)
                {
                    //心跳包不做处理 "98,87,79,98,00,00,00,00"
                    return;
                }
                else
                {
                    //解密20150609
                    byte[] byteTemp = new byte[e.Item.Length - 8];

                    for (int i = 0; i < byteTemp.Length; i++)
                    {
                        byteTemp[i] = e.Item[i + 8];
                    }

                    byteTemp = SocketEncrypt.MaskData(byteTemp, byteTemp.Length);

                    string msg = SocketBufferHelper.ByteBinaryToHexString(byteTemp);

                    message.Type = SocketMessageType.Message;
                    message.Message = msg;

                    if (messageDelegate != null)
                    {
                        messageDelegate(message);
                    }
                }

            }
        }
        #endregion

        #region 发送
        /// <summary>
        ///发送数据
        /// </summary>
        /// <param name="message"></param>
        public void Send(string message)
        {
            if (m_IsConnected)
            {
                Byte[] msgBt = new byte[message.ToCharArray().Length];
                int i = 0;
                foreach (char c in message.ToCharArray())
                {
                    msgBt[i] = Convert.ToByte(c);
                    i++;
                }

                Byte[] bt = new Byte[msgBt.Length + MESSAGE_HEADER_SIZE];

                //加密20150609
                msgBt = SocketEncrypt.MaskData(msgBt, msgBt.Length);

                SocketBufferHelper.EncodeMessage(bt, msgBt, m_header, MESSAGE_HEADER_SIZE);

                SendMessage(bt);
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="bt"></param>
        public void SendMessage(Byte[] bt)
        {
            if (m_IsConnected)
            {
                try
                {
                    skClient.Send(bt);
                }
                catch
                { }
            }
        }

        #endregion



    }
}
